package com.config.ser.service;

import com.config.ser.mapper.CloudConfigMapper;
import com.config.ser.model.CloudConfigDO;
import com.config.ser.request.AddConfigRequest;
import com.config.ser.util.CommonUtil;
import com.config.ser.util.YamlUtils;
import com.config.ser.vo.CheckMD5VO;
import com.config.ser.vo.CloudConfigVO;
import com.mysql.cj.util.StringUtils;
import com.registry.ser.instance.ServiceInstance;
import com.registry.ser.request.RegistryParamModel;
import com.registry.ser.service.RegistryParamServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.stereotype.Service;
import org.yaml.snakeyaml.Yaml;

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CloudConfigService {

    @Autowired(required = false)
    private CloudConfigMapper cloudConfigMapper;

    @Autowired
    private RegistryParamServiceImpl registryParamServiceImpl;
    private static Map<String, Map<String, Object>> map = new HashMap<>();
    /**
     * 校验MD5是否相同
     * @param serverName   服务名
     * @param md5    客户端的md5
     * @return
     */
    public CheckMD5VO checkMD5(String serverName, String md5) {

        // 去数据库取出MD5
        CheckMD5VO checkMD5VO = cloudConfigMapper.checkMD5(serverName);
        if(checkMD5VO == null){
            // 不存在MD5
            return null;
        }else if(checkMD5VO.getMd5().equals(md5)){
            checkMD5VO.setEq(true);
            return checkMD5VO;
        }
        
        //把Yaml文件转换成Map
        Map<String, Object> configMap = this.changeToLinkedMap(serverName, checkMD5VO.getValue());
        checkMD5VO.setConfigValue(configMap);

        System.err.println(checkMD5VO);
        return checkMD5VO;
    }

    /**
     * 添加配置文件
     * @param addConfigRequest
     * @return
     */
    public boolean addConfig(AddConfigRequest addConfigRequest) {

        // 校验是否存在服务名
        CloudConfigDO checkServerName = cloudConfigMapper.selectConfig(addConfigRequest.getServerName());
        if(checkServerName != null){
            return false;
        }
        // 类型转换
        CloudConfigDO cloudConfigDO = new CloudConfigDO();
        BeanUtils.copyProperties(addConfigRequest, cloudConfigDO);
        // 生成MD5
        String realMd5 = CommonUtil.MD5(addConfigRequest.getValue());
        cloudConfigDO.setMd5(realMd5);
        int result = cloudConfigMapper.addConfig(cloudConfigDO);
        return result == 1 ? true : false;
    }

    /**
     * 查询配置文件配置文件
     * @param serverName  服务名
     * @return
     */
    public CloudConfigVO selectConfig(String serverName) {
        CloudConfigDO cloudConfigDO = cloudConfigMapper.selectConfig(serverName);
        CloudConfigVO cloudConfigVO = new CloudConfigVO();
        BeanUtils.copyProperties(cloudConfigDO, cloudConfigVO);
        System.err.println(cloudConfigVO.getValue());
        return cloudConfigVO;
    }

    /**
     * 修改配置文件
     * @param addConfigRequest
     * @return
     */
    public CheckMD5VO updConfig(AddConfigRequest addConfigRequest) {

        // 校验修改后的名字是否已存在
        CloudConfigDO checkServerName = cloudConfigMapper.selectUpdRepeatConfig(addConfigRequest.getServerName(), addConfigRequest.getId());
        if(checkServerName != null){
            return null;
        }
        // 根据id查询修改之前的旧的MD5作为查询就扣的参数
        CloudConfigDO oldCloudConfigDO = cloudConfigMapper.selectConfigById(addConfigRequest.getId());

        // 类型转换
        CloudConfigDO cloudConfigDO = new CloudConfigDO();
        BeanUtils.copyProperties(addConfigRequest, cloudConfigDO);
        // 重新生成MD5
        String realMd5 = CommonUtil.MD5(addConfigRequest.getValue());
        cloudConfigDO.setMd5(realMd5);
        // 根据id进行修改
        int result = cloudConfigMapper.updConfig(cloudConfigDO);
        CheckMD5VO checkMD5VO = new CheckMD5VO();
        if(result == 1) {
            // 这里是查询，所以要用旧的MD5，因为查询接口参数值是要传旧MD5的，和库里面的新MD5对比，不能用新修改的
            checkMD5VO = this.checkMD5(cloudConfigDO.getServerName(), oldCloudConfigDO.getMd5());
            // 如果新旧服务名不同，则从全局map中移除旧服务
            if(!addConfigRequest.getServerName().equals(oldCloudConfigDO.getServerName())){
                map.remove(oldCloudConfigDO.getServerName());
            }
        }else{
            return null;
        }
        return checkMD5VO;
    }

    /**
     * 查询所有配置文件配置文件
     * @return
     */
    public List<CloudConfigVO> selectAllConfig() {
        List<CloudConfigVO> cloudConfigVOS = new ArrayList<>();
        List<CloudConfigDO> cloudConfigDOS = cloudConfigMapper.selectAllConfig();


        cloudConfigVOS = cloudConfigDOS.stream().map(item ->{

            // 查询服务注册情况
            RegistryParamModel registryParamModel = new RegistryParamModel();
            registryParamModel.setServiceName(item.getServerName());
            List<ServiceInstance> serviceInstances = registryParamServiceImpl.serverSelect(registryParamModel);
            String addresses = "";
            if(serviceInstances != null){
                // 把ip拼接起来，并用","分割
                addresses = serviceInstances.stream().map(ServiceInstance::getServiceAddres).collect(Collectors.joining(","));
            }
            CloudConfigVO cloudConfigVO = new CloudConfigVO();
            BeanUtils.copyProperties(item, cloudConfigVO);
            cloudConfigVO.setServiceAddres(addresses);
            return cloudConfigVO;
        }).collect(Collectors.toList());
        return cloudConfigVOS;
    }

    /**
     * 删除配置文件
     * @param serverName  服务名
     * @return
     */
    public boolean delConfig(String serverName) {
        int result = cloudConfigMapper.delConfig(serverName);
        if(result == 1){
            // 删除全局map中的配置
            map.remove(serverName);
            return true;
        }
        return false;
    }

    /**
     * 启动后拉取所有配置，存入内存
     */
    public void pullAllConfig() {
        List<CloudConfigDO> cloudConfigDOS = cloudConfigMapper.selectAllConfig();
        cloudConfigDOS.stream().forEach(item ->{
            // 获取配置文件的Map格式
            Map<String, Object> configMap = changeToLinkedMap(item.getServerName(), item.getValue());
        });
    }

    /**
     * 把Yaml文件转换成Map
     */
    private Map<String, Object> changeToLinkedMap(String serverName, String value){
        Map<String, Object> configMap = new LinkedHashMap<>();
        // 把配置文件字符串转换成.yml文件
        File file = new File(serverName + ".yml");
        try {

            PrintStream ps = new PrintStream(new FileOutputStream(file));
            ps.println(value);// 往文件里写入字符串
//            ps.append("http://www.jb51.net");// 在已有的基础上添加字符串

            // 把文件转换成LinkedHashMap
            YamlUtils yamlUtils = new YamlUtils();
            Map<String, Object> yamlMap;
            yamlMap = yamlUtils.getYamlMap(serverName + ".yml");
            configMap = yamlMap;
            ps.close();

            // 把该配置文件的保存到全局
            map.put(serverName, yamlMap);
            // 删除前，流必须关闭
            new Thread(()->{
                file.delete();
            }).start();

        } catch (Exception e) {
            // TODO Auto-generated catch block
            log.error("ymal文件转换成Map失败");
        }
        return configMap;
    }

    /**
     * 客户端初次启动来拉取配置文件
     * @param serverName 服务名
     * @return
     */
    public CheckMD5VO clientFirstStartPullConfig(String serverName) {

        CloudConfigDO cloudConfigDO = cloudConfigMapper.selectConfig(serverName);
        CheckMD5VO checkMD5VO = new CheckMD5VO();
        BeanUtils.copyProperties(cloudConfigDO, checkMD5VO);
        checkMD5VO.setConfigValue(map.get(serverName));

        // TODO 这里需要加一个刷新该服务所有配置到map，否则只启动客户端，服务端的map并不会刷新，这里为了更高效，所以把配置拉到了内存中
        Map<String, Object> map = changeToLinkedMap(serverName, checkMD5VO.getValue());
        checkMD5VO.setConfigValue(map);
        return checkMD5VO;
    }
}
